package com.fmistuff.threadedcalc;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.Semaphore;

public class ThreadPool implements Runnable
{
	final ConcurrentLinkedQueue<ExpressionNode> evaluationQueue = new ConcurrentLinkedQueue<ExpressionNode>();
	final ConcurrentHashMap<ExpressionNode, EvaluationThread> evaluatedNodes = new ConcurrentHashMap<ExpressionNode, EvaluationThread>();
	final int maxThreads;
	final Semaphore threadCount;
	Thread[] threads;
	EvaluationThread[] ets;
	
	public ThreadPool(int maxThreads)
	{
		this.maxThreads = maxThreads;
		threadCount = new Semaphore(maxThreads);
		threads = new Thread[maxThreads];
		ets = new EvaluationThread[maxThreads];
		
		for (int i=0; i < maxThreads; i++)
		{
			ets[i] = new EvaluationThread();
			threads[i] = new Thread(ets[i]);
			ets[i].thread = threads[i];
			ets[i].pool = this;
		}
	}
	
	public Thread Initialize(ExpressionNode root) throws InterruptedException
	{
		ets[maxThreads - 1].root = root;
		evaluatedNodes.put(root, ets[maxThreads - 1]);
		threadCount.acquire();
		threads[maxThreads - 1].start();
		return threads[maxThreads - 1];
	}
	
	@Override
	public void run()
	{
		while (!Thread.interrupted())
		{
			try
			{
				threadCount.acquire();
			}
			catch (InterruptedException e)
			{
				return;
			}
			
			ExpressionNode node = evaluationQueue.poll();
			
			if (node == null)
				continue;
				
			synchronized (evaluatedNodes)
			{
				if (!evaluatedNodes.containsKey(node))
				{
					int i;
					for(i=0; i < maxThreads; i++)
						if (!threads[i].isAlive())
							break;
					
					ets[i].root = node;
					evaluatedNodes.put(node, ets[i]);
					threads[i].start();
					continue;
				}
			}
		}
	}
}
